package com.jxpanda.spring.module.auth.core.authentication.manager;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.jxpanda.infrastructure.core.tollkit.StringKit;
import com.jxpanda.infrastructure.core.tollkit.json.JsonKit;
import com.jxpanda.infrastructure.spring.toolkit.ReactiveHttpKit;
import com.jxpanda.spring.module.auth.core.authentication.token.CollaborativeAuthenticationToken;
import com.jxpanda.spring.module.auth.core.authentication.token.OAuth2UserCollaborativeAuthenticationToken;
import com.jxpanda.spring.module.auth.core.user.WechatOAuth2User;
import com.jxpanda.spring.module.auth.endpoint.OAuth2Request;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import reactor.core.publisher.Mono;

/**
 * 微信小程序认证管理器
 */
public class WechatOpenPlatformReactiveAuthenticationManager implements CollaborativeReactiveAuthenticationManager {

    private static final String DEFAULT_MINI_PROGRAM_AUTHORIZATION_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

    @Override
    public Mono<CollaborativeAuthenticationToken> authenticate(CollaborativeAuthenticationToken authenticationToken) {
        return Mono.defer(() -> {
            OAuth2Request oAuth2Request = authenticationToken.getOAuth2Request();
            ClientRegistration clientRegistration = authenticationToken.getClientRegistration();
            return fetchAccessToken(clientRegistration, oAuth2Request.getCode())
                    .map(accessToken -> buildAuthenticationToken(accessToken, oAuth2Request, clientRegistration));
        });
    }

    private static OAuth2UserCollaborativeAuthenticationToken buildAuthenticationToken(AccessToken accessToken, OAuth2Request oAuth2Request, ClientRegistration clientRegistration) {
        WechatOAuth2User wechatOauth2User = new WechatOAuth2User(accessToken.getOpenId(), accessToken.getUnionId(), clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName());
        return new OAuth2UserCollaborativeAuthenticationToken(oAuth2Request, clientRegistration, wechatOauth2User);
    }

    private static Mono<AccessToken> fetchAccessToken(ClientRegistration clientRegistration, String code) {
        String authorizationUrl = StringKit.takeFirstNotBlank(clientRegistration.getProviderDetails().getAuthorizationUri(), DEFAULT_MINI_PROGRAM_AUTHORIZATION_URL);
        return ReactiveHttpKit.get(authorizationUrl)
                .queryHandler(query -> {
                    query.put("appid", clientRegistration.getClientId());
                    query.put("secret", clientRegistration.getClientSecret());
                    query.put("code", code);
                    query.put("grant_type", "authorization_code");
                }).build()
                .execute(bodyData -> JsonKit.fromJson(bodyData, AccessToken.class));
    }


    @NoArgsConstructor
    @Data
    private static class AccessToken {


        /**
         * accessToken
         */
        @JsonProperty("access_token")
        private String accessToken;
        /**
         * expiresIn
         */
        @JsonProperty("expires_in")
        private Integer expiresIn;
        /**
         * refreshToken
         */
        @JsonProperty("refresh_token")
        private String refreshToken;
        /**
         * openid
         */
        @JsonProperty("openid")
        private String openId;
        /**
         * scope
         */
        @JsonProperty("scope")
        private String scope;
        /**
         * unionid
         */
        @JsonProperty("unionid")
        private String unionId;
    }

}
